﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using log4net;
using SharpML.Api.Implementation;
using SharpML.Api.Extensions;
using SharpML.Gui.Extensions;
using SharpML.Gui.Properties;
using SharpML.Gui.Data;
using SharpML.Api.Abstraction.Plugin;
using SharpML.Api.Interfaces.Plugin;
using SharpML.Api.Interfaces;
using SharpML.Api.Abstraction;
using SharpML.Api.Enumerations;

namespace SharpML.Gui.Controls
{
    public partial class Master : UserControl
    {
        #region - Private Member Variables -

            /// <summary>
            /// Simple logging pattern
            /// </summary>
            private static readonly ILog Log = LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod().DeclaringType );

            /// <summary>
            /// Simple anti-flood pattern: flag
            /// I'm using volatile to avoid a lock since these are simple flags
            /// </summary>
            private volatile bool InRender = false;

            /// <summary>
            /// Simple anti-flood pattern: no-miss 
            /// I'm using volatile to avoid a lock since these are simple flags
            /// </summary>
            private volatile bool NeedPostRender = false;

            // TODO: Connect active config
            private Configuration ActiveConfiguration;

            /// <summary>
            /// Shortcut to settings
            /// </summary>
            private Settings AppSettings;

            private MLEngine ActiveEngine;

            private PatternDataAdaptor ActiveDataAdaptor;

            private MLTrainer ActiveTrainer;

        #endregion

        #region - Private Propertied and Events -
        
            public event Action ShowLog = delegate {};
            public event Action HideLog = delegate {};

        #endregion

        #region - Construction -

            public Master()
            {
                InitializeComponent();

                AppSettings = Properties.Settings.Default;
                
            }

        #endregion

        #region - Private Functionality -

            private void LoadConfiguration( string something )
            {
                // Manual binding for now due to not having bound the drop down box
                comboBoxAdaptor.Text = AppSettings.SelectedAdaptor;
                comboBoxEngine.Text = AppSettings.SelectedEngine;
                comboBoxTrainer.Text = AppSettings.SelectedTrainer;
            }

            private void SaveConfiguration()
            {
                //new Configuration().LoadFromSettings().SaveToSettings();
                AppSettings.SelectedAdaptor =comboBoxAdaptor.Text;
                AppSettings.SelectedEngine = comboBoxEngine.Text;
                AppSettings.SelectedTrainer =comboBoxTrainer.Text;
            }

            private void DefaultConfiguration()
            {
                Log.Info( "Default configuration.");
#if DEBUG
                AppSettings.LastConfiguration = Path.Combine( "..", "..", "Configurations", "Default.SharpML" );
#else
                AppSettings.LastConfiguration = Path.Combine( "Configurations", "Default.SharpML" );
#endif
                LoadConfiguration( AppSettings.LastConfiguration );

            }

            private void Startup()
            {
                // Load all the plugins
                PluginHost.LoadPlugins( "SharpML.*.*.dll" );

                // Do instantiation
                PluginHost.ProcessPlugins();

                // Update UI
                this.AsyncGuiOp( (Action)delegate {

                    // Data adaptors
                    comboBoxAdaptor.Items.AddRange( PluginHost.PluginComponents.First( plugin => plugin.Key == Api.Enumerations.PluginComponentType.DataAdaptor ).Value.ToArray() );

                    // MLEngines
                    comboBoxEngine.Items.AddRange( PluginHost.PluginComponents.First( plugin => plugin.Key == Api.Enumerations.PluginComponentType.MLEngine ).Value.ToArray() );

                    // Trainers
                    comboBoxTrainer.Items.AddRange( PluginHost.PluginComponents.First( plugin => plugin.Key == Api.Enumerations.PluginComponentType.Trainer ).Value.ToArray() );

                    // Compute Devices
                    foreach( ComputeDevices item in Enum.GetValues( typeof( ComputeDevices ) ) )
                    {
                        comboBoxCompute.Items.Add( item.GetDescriptionFromAttribute() );
                        comboBoxConstruct.Items.Add( item.GetDescriptionFromAttribute() );
                    }

                    // Localize
                    var lastConfig = AppSettings.LastConfiguration;

                    // Finally load the configuration
                    if( !String.IsNullOrEmpty( lastConfig ) && File.Exists( lastConfig ) )
                    {
                        Log.Info( "Loading configuration: " + lastConfig );

                        LoadConfiguration( lastConfig );
                    }
                    else
                        DefaultConfiguration();
                } );
            }
        
            private void PrepareCurrentConfiguration()
            {
                try
                {
                    if( ActiveDataAdaptor != null )
                    {
                        if( ActiveDataAdaptor.PatternData == null )
                            ActiveDataAdaptor.Initialize( textBoxInput.Text );
                    }
                    else
                        return;

                    if( ActiveEngine != null )
                        // TODO: Send Engine
                        ActiveEngine.Initialize( null, ActiveDataAdaptor, textBoxShape.Text );
                    else 
                        return;

                    if( ActiveDataAdaptor.PatternData != null )
                        Log.InfoFormat( "Ready to use {0} on {1}.", ActiveEngine, ActiveDataAdaptor );
                    else
                        Log.Info( "Waiting for data." );
                }
                catch( Exception exception )
                {
                    Log.Error( exception );
                }


            }


            private void LoadConfigGui( IPluginComponent component, string tabName, TabControl.TabPageCollection tabs )
            {
                var gui = (Control)component.Gui;
                if( gui != null )
                {
                    if( !tabs.ContainsKey( tabName ) )
                        tabs.Insert( 1, tabName, component.Name );

                    gui.Dock = DockStyle.Fill;
                    tabs[tabName].Controls.Add( gui );
                }
                else if( tabControl.TabPages.ContainsKey( tabName ) )
                    tabs.RemoveByKey( tabName );
            }


        #endregion

        #region - Base Class Overrides -        

            protected override void OnHandleDestroyed( EventArgs e )
            {
                // Save our settings that aren't in the property collection, or required extra care
                SaveConfiguration();

                // Chain to base
                base.OnHandleDestroyed( e );
            }        
        
        #endregion

        #region - Form Event Handlers -


            private void Master_Load( object sender, EventArgs e )
            {
                // Hookup plugins
                this.AsyncOp( Startup );
            }

            private void tabControl_Selected( object sender, TabControlEventArgs e )
            {
                // TODO: Fix if we're inserting tabs at index 1
                switch( e.TabPageIndex )
                {
                    case 0: // Config
                        // Nothing yet
                        break;
                    case 1: // Data 
                        patternDataVisualizerData.Adaptor = ActiveDataAdaptor;
                        break;

                }
            }

        #endregion

            private void buttonLoadConfig_Click( object sender, EventArgs e )
            {
                // Properties.Settings.Default.Save(
            }

            private void buttonSaveConfig_Click( object sender, EventArgs e )
            {

            }

            private void checkBoxShowLog_CheckStateChanged( object sender, EventArgs e )
            {
                if( checkBoxShowLog.Checked )
                    ShowLog();
                else
                    HideLog();
            }

            private void comboBoxAdaptor_SelectedIndexChanged( object sender, EventArgs e )
            {
                const string tabName = "DataAdaptorConfig";
                var tabs = tabControl.TabPages;

                ActiveDataAdaptor = (PatternDataAdaptor)comboBoxAdaptor.SelectedItem;

                LoadConfigGui( ActiveDataAdaptor, tabName, tabs );

                this.OffThread( PrepareCurrentConfiguration );
            }

            private void comboBoxEngine_SelectedIndexChanged( object sender, EventArgs e )
            {
                const string tabName = "EngineConfig";
                var tabs = tabControl.TabPages;

                ActiveEngine = (MLEngine)comboBoxEngine.SelectedItem;

                LoadConfigGui( ActiveEngine, tabName, tabs );
                

                this.OffThread( PrepareCurrentConfiguration );
            }

            private void comboBoxTrainer_SelectedIndexChanged( object sender, EventArgs e )
            {
                const string tabName = "TrainerConfig";
                var tabs = tabControl.TabPages;

                ActiveTrainer = (MLTrainer)comboBoxTrainer.SelectedItem;

                LoadConfigGui( ActiveTrainer, tabName, tabs );
                

                this.OffThread( PrepareCurrentConfiguration );
            }

            private void textBoxInput_TextChanged( object sender, EventArgs e )
            {
                this.OffThread( PrepareCurrentConfiguration );
            }

            private void comboBoxConstruct_SelectedIndexChanged( object sender, EventArgs e )
            {
                // TODO: Get device count
            }

            private void comboBoxCompute_SelectedIndexChanged( object sender, EventArgs e )
            {

            }

            private void numericUpDownCompute_ValueChanged( object sender, EventArgs e )
            {

            }

            private void numericUpDownConstruct_ValueChanged( object sender, EventArgs e )
            {

            }



    }
}
